import {
  CSBox3Helper,
  CSPoint,
  PickCurve,
  PickedFace,
  pickedFaceData,
  pickedLineData,
  pickedModelData,
  PickedObject,
  pickedPointCloudData,
  pickedVertexData,
  PickLine,
} from '@amcax/base';
import { ctx2, dataManager } from '@app-base/module';
import { nmAppLog, Picker } from '@app-nextmesh/common/utils';
import { ModelTheme } from '../types';

const SELECTED_COLOR = 0xe18b3d;
const SOURCE_SELECTED_COLOR = 0x25b4e2; // 同 --color-sourceAssignment
const TARGET_SELECTED_COLOR = 0xed6f82; // 同 --color-targetAssignment

const COLOR_SELECTED_MAP: Map<ModelTheme, number> = new Map([
  [ModelTheme.Default, SELECTED_COLOR],
  [ModelTheme.Source, SOURCE_SELECTED_COLOR],
  [ModelTheme.Target, TARGET_SELECTED_COLOR],
]);

// 已选中的线、面或体类型，map的key是id
type PickedData =
  | pickedVertexData
  | pickedPointCloudData
  | pickedLineData
  | pickedFaceData
  | pickedModelData;
type PickedObj =
  | CSPoint
  | PickCurve
  | PickLine
  | PickedFace
  | PickedObject
  | CSBox3Helper;
type selectedMeshesType = Map<string, PickedData>;
// 不同ModelTheme下，选中的模型Map
const selectedMeshes: Map<ModelTheme, selectedMeshesType> = new Map([
  [ModelTheme.Default, new Map<string, PickedData>()],
  [ModelTheme.Source, new Map<string, PickedData>()],
  [ModelTheme.Target, new Map<string, PickedData>()],
]);

const getAllSelectedMeshesId = () => {
  const allSelectedMeshesId: string[] = [];
  for (const theme of selectedMeshes.keys()) {
    const selectedMeshesId = Array.from(selectedMeshes.get(theme).keys());
    allSelectedMeshesId.push(...selectedMeshesId);
  }
  return allSelectedMeshesId;
};

const clearSelectedMeshes = () => {
  Picker().clickNull();
  for (const theme of selectedMeshes.keys()) {
    selectedMeshes.get(theme).clear();
  }
};

const getColor = (theme: ModelTheme): number => {
  const color = COLOR_SELECTED_MAP.get(theme);
  if (color != null) {
    return color;
  }
  return SELECTED_COLOR;
};

/**
 * 获取选中/高亮的数据的id，此id为modelId，也就是一个model的唯一标识（点、线、面、体的一致）
 * @param pickedData 被选中的数据
 * @returns id
 */
const getPickedDataId = (pickedData: PickedData) => {
  return pickedData.idInfo.o;
};

/**
 * 获取选中/高亮物体的id，点、线、面、体的id各不相同
 * @param obj
 * @returns
 */
const getAssisObjId = (obj: PickedObj): string => {
  let id: string;
  if (obj['lineId'] != null) {
    id = obj['lineId'];
  } else {
    id = obj['modelDxid'];
  }
  return id;
};

const setCSPointType = (obj: CSPoint, color: number) => {
  const pointType = color === SOURCE_SELECTED_COLOR ? 'select' : 'mid';
  obj.loadTexture(pointType);
};

/**
 * 处理选中的Obj，并返回选中的Obj id数组
 */
const handleSelect = (
  picked: PickedData,
  theme: ModelTheme = ModelTheme.Default,
  cancelSelected = true, // 取消已选中的
): string[] => {
  nmAppLog('handleSelect', picked);
  const obj = picked?.assisObj;
  if (obj == null) {
    return [];
  }
  const objId = getAssisObjId(obj);
  const color = getColor(theme);
  const oldPicedData = selectedMeshes.get(theme).get(objId);

  if (oldPicedData) {
    if (cancelSelected) {
      // 已经选中，则取消选中，将当前选中的模型高亮
      // 从 已选中 中移除
      unselectObject(theme, oldPicedData, objId, obj);
    }
    if (obj instanceof CSPoint) {
      Picker().unselectPoint(picked as pickedVertexData);
    } else if (obj instanceof PickCurve || obj instanceof PickLine) {
      Picker().unselectLine(picked as pickedLineData);
    } else if (obj instanceof PickedFace) {
      Picker().unselectFace(picked as pickedFaceData);
    } else if (obj instanceof PickedObject) {
      Picker().unselectModel(picked as pickedModelData);
    }
  } else {
    // 单选拾取----点，线，面，体
    selectedMeshes.get(theme).forEach((item) => {
      const pastObjId = getAssisObjId(item.assisObj);
      const oldPickedData = selectedMeshes.get(theme).get(pastObjId);
      if (theme === ModelTheme.Default && oldPickedData) {
        unselectObject(theme, oldPickedData, pastObjId, obj);
      }
    });

    selectedMeshes.get(theme).set(objId, picked);
    if (obj instanceof CSPoint) {
      setCSPointType(obj, color);
    } else if (obj instanceof CSBox3Helper) {
      nmAppLog('do nothing');
    } else {
      obj.selected(color);
    }
  }
  ctx2.viewer.requestRender();
  return Array.from(selectedMeshes.get(theme).keys()).filter(
    (id) => id !== undefined,
  );
};

const unselectObject = (
  theme: ModelTheme,
  oldPickedData: PickedData,
  objId: string,
  obj: PickedObj,
) => {
  selectedMeshes.get(theme).delete(objId);
  if (oldPickedData.assisObj.uuid !== obj.uuid) {
    oldPickedData.assisObj.destroy();
  }
};

/**
 * 根据用户（表单中）已经选中/取消选中的模型，在场景中选中、取消选中对应的模型
 * @param ids id数组
 * @param theme 主题
 * @param clearSelected 是否清除选中
 */
const selectMeshes = (
  ids: string[],
  theme?: ModelTheme,
  clearSelected?: boolean,
) => {
  const color = getColor(theme);
  if (clearSelected) {
    clearSelectedMeshes();
  }
  setTimeout(() => {
    // TODO 必须要用setTimeout延时，不然无法添加到assisGroup中，也就无法渲染，暂时不知道什么原因
    // TODO 且如果需要选中的元素较多，会有明显的卡顿
    // 对比已选中的数据，找到需要添加/删除的数据
    const themeMeshes = selectedMeshes.get(theme);

    // 去重
    ids = Array.from(new Set(ids));

    const addIds = ids.filter((id) => !themeMeshes.has(id));
    const deleteIds = Array.from(themeMeshes.keys()).filter(
      (id) => !ids.includes(id),
    );
    const pickedDataMap: Map<string, PickedData> = new Map();
    for (let i = 0; i < addIds.length; i++) {
      const id = addIds[i];
      const pickedData: PickedData = Picker().generatePickObjByPickID(id);
      if (pickedData) {
        pickedDataMap.set(id, pickedData);
      }
    }
    // 添加到assisGroup和selectedMeshes
    for (const [objId, pickedData] of pickedDataMap) {
      const modelId = getPickedDataId(pickedData);
      const objectInfo = dataManager.getObjectInfoByDxid(modelId);
      if (!objectInfo) {
        continue;
      }
      // 根据模型的可见性，设置assisObj的可见性
      pickedData.assisObj.visible = objectInfo.visibility;
      pickedData.assisObj.addTo(ctx2.cadScene.assisGroup);
      const obj = pickedData.assisObj;

      if (obj instanceof CSPoint) {
        setCSPointType(obj, color);
      } else if (obj instanceof CSBox3Helper) {
        nmAppLog('do nothing');
      } else {
        obj.selected(color);
      }
      themeMeshes.set(objId, pickedData);
    }
    // 从assisGroup和selectedMeshes中移除
    for (const id of deleteIds) {
      const pickedData = themeMeshes.get(id);
      if (pickedData?.assisObj) {
        pickedData.assisObj.destroy();
        themeMeshes.delete(id);
      }
    }
    ctx2.viewer.requestRender();
  });
};

export {
  clearSelectedMeshes,
  getAllSelectedMeshesId,
  handleSelect,
  selectMeshes,
};
